// 进入CPL=3的模式，模拟了中断返回操作
// 先压入了SS/ESP/EFLAGS/CS/EIP，再执行iret返回指令
// 然后再重新设置数据段寄存器
// #define move_to_user_mode() 
static inline void move_to_user_mode(void) {
	// 注意，这里涉及特权级的变化，但是栈仍然用原来的这个，所以执行到这里后能够正确的返回
	// 否则，如果栈发生变化的话，那么就能从move_to_user_mode正确返回，因为此处没有相应的返回地址
	__asm__ __volatile__(
		"movl %%esp,%%eax\n\t"		// esp不能直接压入，所以先保存到eax中，后面再压入
		"pushl $0x17\n\t"		// 压入SS
		"pushl %%eax\n\t"		// 压入当前的ESP
		"pushfl\n\t" 			// EFLAGS
		"pushl $0x0f\n\t"		// CS
		"pushl $1f\n\t" 		// EIP：即跳转到下面的1处开始运行
		"iret\n"				// IRET: 返回到1F处跑，但特权级变成了3
		"1:\tmovl $0x17,%%eax\n\t" 	// 重设数据段
		"movw %%ax,%%ds\n\t" 
		"movw %%ax,%%es\n\t" 
		"movw %%ax,%%fs\n\t"
		"movw %%ax,%%gs"
		:::"ax");
}

// 各指令的简单封装, 开关中断及中断返回指令等
#define sti() __asm__ __volatile__("sti"::)
#define cli() __asm__ __volatile__("cli"::)
#define nop() __asm__ __volatile__("nop"::)
#define iret() __asm__ __volatile__("iret"::)

// 设置门描述符：写得有点复杂，完全可以用C来做
// 设置中断门或陷阱门、DPL权限、被调函数的地址addr，门描述符的地址gate_addr
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ __volatile__("movw %%dx,%%ax\n\t" \
	"movw %0,%%dx\n\t" \
	"movl %%eax,%1\n\t" \
	"movl %%edx,%2" \
	: \
	: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
	"o" (*((char *) (gate_addr))), \
	"o" (*(4+(char *) (gate_addr))), \
	"d" ((char *) (addr)),"a" (0x00080000))

// 中断门：DPL=0,不允许用户访问
// 中断门会清除 EFLAGS 中的 IF 位，即不允许中断再次发生
// 整个代码中未使用，可能是想允许整个中断发生吧
#define set_intr_gate(n,addr) \
	_set_gate(&idt[n],14,0,addr)

// 陷阱门：DPL=0, 不清除IF，即允许中断发生
#define set_trap_gate(n,addr) \
	_set_gate(&idt[n],15,0,addr)

// 陷阱门，DPL=3，允许用户访问：DPL=3, 不清除IF，即允许中断发生
#define set_system_gate(n,addr) \
	_set_gate(&idt[n],15,3,addr)

// 设置代码段、数据等描述符
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
	*(gate_addr) = ((base) & 0xff000000) | \
		(((base) & 0x00ff0000)>>16) | \
		((limit) & 0xf0000) | \
		((dpl)<<13) | \
		(0x00408000) | \
		((type)<<8); \
	*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \
		((limit) & 0x0ffff); }

// 设置TSS和LDT描述符
// 以下是设置GDT表项，将类型设置成tss或者ldt类型，由于这两项还要指向另一个表
// 因此addr代表相应的表项指针, type代表表项的类型, n为指定的表项地址
#define _set_tssldt_desc(n,addr,type) \
__asm__ __volatile__("movw $104,%1\n\t" \
	"movw %%ax,%2\n\t" \
	"rorl $16,%%eax\n\t" \
	"movb %%al,%3\n\t" \
	"movb $" type ",%4\n\t" \
	"movb $0x00,%5\n\t" \
	"movb %%ah,%6\n\t" \
	"rorl $16,%%eax" \
	::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
	 "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
	)

// 设置指定的GDT表项为TSS或者LDT类型
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x82")

